Scope down to manual-URL spine; make compose verifiable end-to-end - #6
Conversation
Two phone-friendly ways to run real discovery and produce three composed
MP4s alongside their source audio and clip assets:
- .github/workflows/build-clips.yml: manual-dispatch workflow that checks
for API tokens, installs ffmpeg/yt-dlp, builds the CLI, runs discovery
and compose via scripts/build-clips.sh, and uploads a clips/ artifact.
- .devcontainer/: Rust+Python devcontainer with a post-create step that
wires yt-dlp into ~/.capcut-cli/bin and builds the release binary; pairs
with `make clips` for one-command runs inside Codespaces.
- scripts/build-clips.sh: discovers one trending sound + three ranked X
clips, imports each, composes clip_{1,2,3}.mp4, and stages the real
source references plus a manifest.json under ./clips.
- README: documents both paths and the required repo/Codespace secrets.
The CLI is Rust-only; yt-dlp itself is distributed as a standalone Linux binary by the upstream project, and 'deps install' already downloads it. Using that path keeps Python off the install surface for both the Action and the devcontainer.
Product decision: discovery depends on gated APIs (TikTok Research access, X paid-tier search) whose availability we can't guarantee. Treat "fresh input in, finished clip out" as the honest minimum viable truth and make the compose pipeline the solid thing the tool does. - README rewritten around the manual-URL flow. The Quick Start, batch guide, and command reference all center on library import + compose. The discover/autopilot commands are moved into an "Optional: API-gated discovery (experimental)" section with explicit caveats about token gating. Stale claims removed. Python references stripped. - scripts/build-clips-from-urls.sh: primary batch path. Takes one supplied sound URL plus three supplied clip URLs and produces a self-contained clips/ folder with finished MP4s, source references, and a provenance manifest. - .github/workflows/build-clips.yml: gains a mode selector; urls mode is the default, discovery mode is opt-in behind both repo secrets. - tests/e2e_url_to_clip.rs: end-to-end smoke test that exercises the full library import → compose pipeline via a yt-dlp shim, so the honest minimum viable truth is verifiable without network access. - .github/workflows/test.yml: runs cargo test --all-targets on every push so the spine stays provable in CI. - library/manifest.json plus small synthetic demo fixtures under library/sounds/assets/snd_demo001 and library/clips/clp_demo001 so compose works immediately on a fresh clone and the compose smoke test has real bytes to exercise. - src/config.rs: CAPCUT_YTDLP_PATH env override so tests can inject a yt-dlp shim. Production path unchanged. - src/media/downloader.rs: merge two duplicate #[cfg(test)] mod tests blocks that were preventing cargo test from compiling at all. - .gitignore: trimmed Python section, whitelisted the committed demo fixtures, anchored the batch-output ignore to the repo root.
- .env.example: reframe TWITTER_BEARER_TOKEN and TIKTOK_RESEARCH_ACCESS_TOKEN as optional (they only affect API-gated discovery, not the primary library import + compose flow). Note the pricing reality for the X token. Document the new CAPCUT_YTDLP_PATH test hook. - library/sounds/README.md: reflect the actual directory layout (assets/ is where imports land), mark the nested manifest.json as a legacy seed, and point readers at the authoritative library/manifest.json. - .devcontainer/post-create.sh: drop the Python mention from the header comment now that the bootstrap is entirely Rust-native.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 61087f6ddd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Ok(p) = std::env::var("CAPCUT_YTDLP_PATH") { | ||
| return PathBuf::from(p); |
There was a problem hiding this comment.
Treat empty CAPCUT_YTDLP_PATH as unset
ytdlp_path() now returns the CAPCUT_YTDLP_PATH value whenever the variable exists, even if it is an empty string. In that case downstream dependency flows break (for example, deps install reaches download_ytdlp() with an empty destination path and can panic on dest.parent().unwrap()), so users who source a .env file containing CAPCUT_YTDLP_PATH= get a hard failure instead of the default ~/.capcut-cli/bin/yt-dlp behavior.
Useful? React with 👍 / 👎.
| --argjson sound "$(echo "$SOUND_JSON" | jq '.data.sounds[0]')" \ | ||
| --argjson clips "$(echo "$CLIPS_JSON" | jq "[.data.clips[0:${#CLIP_IDS[@]}][]]")" \ |
There was a problem hiding this comment.
Persist actual imported candidates in discovery manifest
The manifest is labeled as provenance for the generated clips, but it always stores .data.sounds[0] and the first N discovered clips rather than the candidates that were actually imported. When early ranked URLs fail import and later ones succeed (which this script explicitly supports), clips/manifest.json records different sources than the MP4s were built from, making reruns/debugging unreliable.
Useful? React with 👍 / 👎.
Summary
Product decision from the PO: discovery depends on gated APIs (TikTok Research access, X paid-tier search) we can't guarantee, so stop pretending it's the spine. Treat "fresh input in, finished clip out" as the honest minimum viable truth and make the compose pipeline the solid thing the tool does.
library import+compose. Thediscover/autopilotcommands move to "Optional: API-gated discovery (experimental)" with explicit token caveats (TikTok Research gating, X API at $200/mo minimum). Stale claims removed. Python references stripped.scripts/build-clips-from-urls.shis the new primary batch path: 1 supplied sound URL + 3 supplied clip URLs → a self-containedclips/folder with finished MP4s, source references, and a provenance manifest..github/workflows/build-clips.ymlgains amodeselector —urlsis the default;discoveryis opt-in behind both repo secrets.tests/e2e_url_to_clip.rsis a real end-to-end smoke test oflibrary import→composevia a yt-dlp shim, so the manual-URL spine is verifiable in CI without network. Passes in ~7s..github/workflows/test.ymlrunscargo test --all-targetson every push.library/manifest.jsonsocomposeworks on a fresh clone and the existing compose smoke test has real bytes.#[cfg(test)] mod testsinsrc/media/downloader.rswas preventingcargo testfrom compiling at all. Renamed the second module totests_url_detection.src/config.rsgains aCAPCUT_YTDLP_PATHenv override so tests can inject a yt-dlp shim. Production path unchanged..devcontainer/rebuilt for Rust-only bootstrap (no Python toolchain), usingcapcut-cli deps installto fetch the standalone yt-dlp binary..env.example,library/sounds/README.md,.devcontainer/post-create.sh) aligned with the new scope.67 tests passing locally (66 unit + 1 e2e).
Test plan
cargo test --all-targets— 67 tests pass./target/release/capcut-cli deps check— reports ffmpeg + yt-dlp installedtests/e2e_url_to_clip.rs— imports two fake URLs via a yt-dlp shim, composes them, asserts the output MP4 existsscripts/build-clips-from-urls.sh— bash syntax check, error paths surface upstream CLI errors cleanly.github/workflows/build-clips.yml—urlsmode validates presence of all four URL inputs;discoverymode validates both token secretsbuild-clipsAction inurlsmode against real URLs (pending, needs user to trigger from GitHub mobile app)build-clipsAction indiscoverymode if/when TikTok Research + X Basic tokens are available